<?php

class MY_Model extends CI_Model {

	protected $table;
	protected $as;
	protected $relations = array();
	protected $columns = array();

	public function __construct() {
		parent::__construct();

		$this->load->database();
	}

	public function records($columns = array(), $conditions = array(), $orders = array(), $limit = 0, $offset = 0) {
		$this->prepare_columns($columns);
		$this->prepare_relations();
		$this->prepare_conditions($conditions);
		$this->prepare_orders($orders);
		if ($limit) {
			$this->db->limit($limit, $offset);
		}

		$query = $this->db->get();

		return $query->result();
	}

	public function total($conditions = array()) {
		$this->prepare_relations();
		$this->prepare_conditions($conditions);
		return $this->db->count_all_results();
	}

	public function find($conditions) {
		if (is_numeric($conditions)) {
			$conditions = array(
				array('where', 'id', $conditions)
			);
		}

		$rows = $this->records(array(), $conditions, array(), 1, 0);
		return ($rows && isset($rows[0])) ? $rows[0] : false;
	}

	public function insert($data) {
		$this->db->insert($this->table, $data);
		return $this->db->insert_id();
	}

	public function insert_batch($data) {
		return $this->db->insert_batch($this->table, $data);
	}

	public function update($id, $data) {
		$this->db->where('id', $id);
		return $this->db->update($this->table, $data);
	}

	public function update_batch($data, $condition) {
		return $this->db->update_batch($this->table, $data, $condition);
	}

	public function delete($conditions) {
		if (is_numeric($conditions)) {
			$conditions = array(
				array('where', 'id', $conditions)
			);
		}

		$this->prepare_conditions($conditions, false);

		return $this->db->delete($this->table);
	}

	public function truncate(){
		$this->db->truncate($this->table);
	}

	private function prepare_relations() {
		$this->db->from($this->table());
		if (isset($this->relations[0])) {
			foreach ($this->relations as $relation) {
				$on = "{$this->as}.{$relation['local']}={$relation['as']}.{$relation['foreign']}";
				$this->db->join("{$relation['table']} {$relation['as']}", $on, $relation['type']);
			}
		}
	}

	private function prepare_columns($columns = array()) {
		$select_strings = array();

		if (!isset($columns[0])) {
			$columns = $this->columns;
		}

		foreach ($columns as $column) {
			$prefix = $this->prefix($column[0]);
			if(is_array($column)){
				$select_strings[] = $prefix . $column[0] . (isset($column[1]) ? ' AS ' . $column[1] : '');
			}else{
				$select_strings[] = $prefix . $column;
			}
		}

		if (isset($select_strings[0])) {
			$this->db->select(implode(',', $select_strings));
		} else {
			$this->db->select('*');
		}
	}

	private function prepare_conditions($conditions = array(), $as = true) {
		if (isset($conditions[0])) {
			foreach ($conditions as $condition) {
				$prefix = $as ? $this->prefix($condition[1]) : '';
				call_user_func_array(array($this->db, $condition[0]), array($prefix . $condition[1], $condition[2]));
			}
		}
	}

	private function prepare_orders($orders = array()) {
		if (isset($orders[0])) {
			foreach ($orders as $order) {
				$prefix = $this->prefix($order[0]);
				$this->db->order_by($prefix . $order[0], $order[1]);
			}
		}
	}

	private function table() {
		return $this->table . (empty($this->as) ? '' : (' ' . $this->as));
	}

	private function prefix($column) {
		return strpos($column, '.') === false ? (empty($this->as) ? '' : ($this->as . '.')) : '';
	}

}